#include "jgooglepinyin.h"
#include "GooglePinYin/pinyinime.h"
#include <QTECore/jlog.h>

#include <QThreadPool>
#include <QStringList>
#include <QDateTime>
#include <QSettings>
#include <QTimer>

using namespace ime_pinyin;

namespace QTE
{

class Q_DECL_HIDDEN JGooglePinyinPrivate
{
public:
	explicit JGooglePinyinPrivate(JGooglePinyin *q_ptr);

public:
	void flushChineseList(int count);

public:
	JGooglePinyin *q_ptr;
	QStringList m_lstEN;
	int m_chooseCounter = 0;
};

JGooglePinyinPrivate::JGooglePinyinPrivate(JGooglePinyin *q_ptr) :
	q_ptr(q_ptr)
{

}

void JGooglePinyinPrivate::flushChineseList(int count)
{
	if( count > LIST_MAX_NUM )
		count = LIST_MAX_NUM;

	q_ptr->chineseList.clear();
	char16 buf[33] = {0};

	for(int i=0; i<count; i++)
	{
		im_get_candidate(i, buf, 32); //获取汉字结果（一条最多32个汉字）

#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
		q_ptr->chineseList << QString::fromUtf16(buf);
#else
		q_ptr->chineseList << QString::fromUtf16(J_RCT(char16_t*, buf));
#endif
	}
}

/*-------------------------------------------------------------------------------------------------------------------*/

#ifdef Q_OS_WIN
# define CONFIG_PATH  "./googlepinyin/"
#else
# define CONFIG_PATH  "/etc/googlepinyin/"
#endif

JGooglePinyin::JGooglePinyin() :
	d_ptr(new JGooglePinyinPrivate(this))
{
	chineseList.reserve(LIST_MAX_NUM);

	QSettings setter(CONFIG_PATH "pinyinEx.ini", QSettings::IniFormat);
	d_ptr->m_lstEN = setter.value("pyEn", d_ptr->m_lstEN).toStringList();

	if( d_ptr->m_lstEN.size() <= 1 )
	{
		jError() << "*** Can't load pyEn!";
		return;
	}

	bool ret = im_open_decoder(CONFIG_PATH "dict_pinyin.dat", CONFIG_PATH "user.dat");
	if (!ret)
	{
		jError() << "*** open dict faild";
		return;
	}
	im_init_user_dictionary(CONFIG_PATH "user.dat");
}

#if 0
GooglePinyin::~GooglePinyin()
{
	im_close_decoder();
	delete d_ptr;
}
#endif

static int indexOfAscii(const QString &gemfield)
{
	for(int i=0; i<gemfield.size(); i++)
	{
		ushort unic = gemfield[i].unicode();
		if( unic < 0x4E00 or unic > 0x9FA5 )
			return i;
	}
	return -1;
}

class Q_DECL_HIDDEN Runnable : public QRunnable
{
public:
	void run() override
	{
		im_flush_cache();
	}
};

QString JGooglePinyin::searchCN(const QString &gemfield)
{
	chineseList.clear();
	pinYinCache = "";
	pinYinText = "";

	if( gemfield.isEmpty() )
		return QString();

	int index = indexOfAscii(gemfield);
	if( index < 0 )
		return QString();

	im_reset_search(); //重置搜索

	// 搜索
	QString pinyin = gemfield.mid(index);
	d_ptr->flushChineseList(im_search(pinyin.toLatin1().data(), pinyin.size()));

	static int i = 0;
	if( i++ % 10 == 0 )
		QThreadPool::globalInstance()->start(new Runnable());

	size_t decodedLen = 0;
	const char *sps = im_get_sps_str(&decodedLen);
	this->pinYinCache = QString::fromStdString(std::string(sps, decodedLen));

	const uint16 *pos; // 获取分词结果
	int size = im_get_spl_start_pos(pos);

	for(int i=size-1; i>0; i--)
		pinyin.insert(pos[i], "'"); //分词
	pinyin.replace("''", "'");

	d_ptr->m_chooseCounter = 0;
	pinYinText = pinyin;

	return pinyin;
}

int JGooglePinyin::choose(int index)
{
	d_ptr->flushChineseList(im_choose(index));
	return ++d_ptr->m_chooseCounter;
}

int JGooglePinyin::cancelChoose()
{
	if( d_ptr->m_chooseCounter > 0 )
	{
		d_ptr->flushChineseList(im_cancel_last_choice());
		return --d_ptr->m_chooseCounter;
	}
	return -1;
}

void JGooglePinyin::binarySearchEN(const QString &gemfield)
{
	int min = 0;
	int max = d_ptr->m_lstEN.size();
	int idx = max / 2;

	while (true)
	{
		if (d_ptr->m_lstEN[idx].startsWith(gemfield, Qt::CaseInsensitive))
			break;
		if (max == min + 1 || max <= min || max == idx + 1 || max == idx || min == idx + 1 || min == idx )
			break;
		if (d_ptr->m_lstEN[idx].toLower() > gemfield)
			max = idx;
		else
			min = idx;
		idx = (max + min) / 2;
	}
	do{
		if (--idx < 0)
			break;
	}while(d_ptr->m_lstEN[idx].startsWith(gemfield, Qt::CaseInsensitive));

	++idx;
	int cnt = 0;
	while(++cnt < 100)
	{
		if (idx >= d_ptr->m_lstEN.size())
			break;
		if (d_ptr->m_lstEN[idx].startsWith(gemfield, Qt::CaseInsensitive))
			chineseList.append(d_ptr->m_lstEN[idx]);
		else
			break;
		idx++;
	}
}

void JGooglePinyin::matching(const QString &gemfield, bool isEn)
{
	chineseList.clear();
	if (isEn)
		binarySearchEN(gemfield);
	else
		searchCN(gemfield);
}

// 预测模型最多N个字
#define MAX_PREDICTS_NUM  5

void JGooglePinyin::predicts(const QString &content)
{
	chineseList.clear();
//	im_reset_search(); //重置搜索

	if( content.isEmpty() or QTE::isPureAscii(content) )
		return ;

	int index = 0;
#ifdef MAX_PREDICTS_NUM
	if( content.size() > MAX_PREDICTS_NUM )
		index = content.size() - MAX_PREDICTS_NUM;
#endif

	static char16 unic[MAX_PREDICTS_NUM] = {0};
	static int lastSize = 0;
	int i = 0;

	for(; index<content.size(); index++, i++)
		unic[i] = static_cast<char16>(content[index].unicode());

	for(; i<lastSize; i++)
		unic[i] = 0;
	lastSize = content.size();

	char16 (*pre_buf)[kMaxPredictSize + 1];
	size_t num = im_get_predicts(unic, pre_buf);

	if( num > LIST_MAX_NUM )
		num = LIST_MAX_NUM;

#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
	for(size_t i=0; i<num; i++)
		chineseList << QString::fromUtf16(pre_buf[i]);
#else
	for(size_t i=0; i<num; i++)
		chineseList << QString::fromUtf16(J_RCT(char16_t*, pre_buf[i]));
#endif
}

} //namespace QTE
